﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace SoSlam.Common.ClientLogic
{
    public class AnimatedSprite
    {
        /// <summary>
        /// Current position in the sprite sheet
        /// </summary>
        public Vector2 Position;

        /// <summary>
        /// Game
        /// </summary>
        public Game Game;

        /// <summary>
        /// Current animation definition
        /// </summary>
        protected AnimationDefinition _definition;
        public AnimationDefinition Definition { get { return _definition; } }

        /// <summary>
        /// Sprite batch
        /// </summary>
        protected SpriteBatch _spriteBatch;

        /// <summary>
        /// Texture of the current sprite
        /// </summary>
        protected Texture2D _sprite;

        /// <summary>
        /// Current frame
        /// </summary>
        protected Point _currentFrame;

        /// <summary>
        /// True if the animation is finished, false otherwise
        /// </summary>
        protected bool _finishedAnimation = false;

        /// <summary>
        /// Time between frames
        /// </summary>
        protected double _timeBetweenFrame = 16; // 60 fps

        /// <summary>
        /// Last frame updated time
        /// </summary>
        protected double _lastFrameUpdatedTime = 0;

        public float Scale { get { return _definition.Scale; } set { _definition.Scale = value; } }

        public float Width { get { return _definition.FrameSize.X * Scale; } }
        public float Height { get { return _definition.FrameSize.Y * Scale; } }

        public Color Color { get; set; }

        private int _framerate = 60;
        /// <summary>
        /// Frame rate
        /// </summary>
        public int Framerate
        {
            get { return this._framerate; }
            set
            {
                if (value <= 0)
                    throw new ArgumentOutOfRangeException("Framerate can't be less or equal to 0");

                if (_framerate != value)
                {
                    _framerate = value;
                    _timeBetweenFrame = 1000.0d / (double)_framerate;
                }
            }
        }

        public bool IsActive { get; set; }

        public AnimatedSprite(Game game, AnimationDefinition definition)
            :this(game, definition, true)
        {
        }

        public AnimatedSprite(Game game, AnimationDefinition definition, bool isActive)
        {
            this.Game = game;

            IsActive = isActive;
            _definition = definition;
            _currentFrame = new Point();

            Color = Color.White;
            Position = new Vector2();
        }

        public void Initialize()
        {
            this.Framerate = _definition.FrameRate;
        }

        /// <summary>
        /// Resets the animation
        /// </summary>
        public void Reset()
        {
            this._currentFrame = new Point(0, 0);
            this._finishedAnimation = false;
            this._lastFrameUpdatedTime = 0;
        }

        /// <summary>
        /// Loads the content
        /// </summary>
        /// <param name="spritebatch"></param>
        public void LoadContent()
        {
            LoadContent(null);
        }

        /// <summary>
        /// Loads the content
        /// </summary>
        /// <param name="spritebatch"></param>
        public void LoadContent(SpriteBatch spritebatch)
        {
            _sprite = Game.Content.Load<Texture2D>(_definition.AssetName);
            if (spritebatch == null)
                _spriteBatch = new SpriteBatch(this.Game.GraphicsDevice);
            else
                _spriteBatch = spritebatch;
        }

        /// <summary>
        /// Updates the data for animation purpose
        /// </summary>
        /// <param name="gameTime"></param>
        public void Update(GameTime gameTime)
        {
            if (_finishedAnimation) return;

            if (IsActive)
            {
                _lastFrameUpdatedTime += gameTime.ElapsedGameTime.Milliseconds;
                if (_lastFrameUpdatedTime > _timeBetweenFrame)
                {
                    _lastFrameUpdatedTime = 0;
                    if (_definition.Loop)
                    {
                        _currentFrame.X++;
                        if (_currentFrame.X >= _definition.NbFrames.X)
                        {
                            _currentFrame.X = 0;
                            _currentFrame.Y++;
                            if (_currentFrame.Y >= _definition.NbFrames.Y)
                                _currentFrame.Y = 0;
                        }
                    }
                    else
                    {
                        _currentFrame.X++;
                        if (_currentFrame.X >= _definition.NbFrames.X)
                        {
                            _currentFrame.X = 0;
                            _currentFrame.Y++;
                            if (_currentFrame.Y >= _definition.NbFrames.Y)
                            {
                                _currentFrame.X = _definition.NbFrames.X - 1;
                                _currentFrame.Y = _definition.NbFrames.Y - 1;
                                _finishedAnimation = true;
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Displays the animation
        /// </summary>
        /// <param name="gameTime"></param>
        public void Draw(GameTime gameTime)
        {
            Draw(gameTime, true);
        }
        
        /// <summary>
        /// Displays the animation
        /// </summary>
        /// <param name="gameTime"></param>
        public void Draw(GameTime gameTime, bool doBeginEnd)
        {
            if (doBeginEnd) _spriteBatch.Begin();

            _spriteBatch.Draw(_sprite,
                this.Position - new Vector2(0, _definition.FrameSize.Y * Scale),
                new Rectangle(_definition.Offset.X + _currentFrame.X * _definition.FrameSize.X, _definition.Offset.Y + _currentFrame.Y * _definition.FrameSize.Y, _definition.FrameSize.X, _definition.FrameSize.Y),
                this.Color,
                0f,
                new Vector2(),
                this.Scale,
                SpriteEffects.None,
                0f);

            if (doBeginEnd) _spriteBatch.End();
        }
    }
}
